import { UiFrameworkExtension, ProvidedBy, ConfigSpec } from '../../components'
import { Link, Warning } from '@brillout/docpress'

<ConfigSpec
  env="server"
  cumulative
  providedBy={<ProvidedBy noCustomGuide />}
/>

The `+Head` setting allows you to add `<head>` tags to your pages.

> See <Link href="/head-tags" /> for a general introduction about `<head>` tags.

> See also the <Link href="/useConfig#config-head">`<Head>` component</Link> (not to be confused with the `+Head` setting).

Common use cases:

```tsx
// /pages/+Head.ts
// Environment: server

import previewImage from './previewImage.jpg'
import favicon from './favicon.png'
import iconMobile from './iconMobile.png'

export function Head() {
  return (
    <>
      {/* Icon shown in the browser tab (aka favicon) */}
      <link rel="icon" href={favicon} type="image/svg+xml" />

      {/* Icon shown on mobile homescreens (PWA) */}
      <link rel="apple-touch-icon" href={iconMobile} type="image/svg+xml" />

      {/* Add script tag */}
      <script type="text/javascript" src="https://example.com/some-script.js"></script>

      {/* Image shown when sharing on social sites (Twitter, WhatsApp, ...) */}
      <meta property="og:image" content={previewImage} />
      {/* More Open Graph tags */}
      <meta property="og:type" content="website" />
      <meta property="article:author" content="https://example.com/author" />

      {/* Settings for search engine crawlers */}
      <meta name="robots" content="index, follow" />

      {/* PWA settings */}
      <meta name="theme-color" content="#00f" />
      <link rel="manifest" href="/manifest.webmanifest" />

      {/* CSP setting */}
      <meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
    </>
  )
}
```

> The `+Head` setting **only applies to the HTML of the first page the user visits** — it doesn't update upon client-side navigation. Therefore, you should use `+Head` only for `<head>` tags that are:
> - Global (e.g. favicon and PWA settings) — their value is the same across all pages and therefore doesn't need to be updated upon navigation.
> - Page-specific but intended for crawlers (e.g. SEO and SMO tags) — since crawlers extract information from the HTML of pages.
>
> For example:
> - You cannot use `+Head` for `<title>` (use <Link href="/title">`+title`</Link> instead), because `<title>` sets the title shown in the user's browser tab which typically changes upon client-side navigation.
> - You can use `+Head` for `<meta property="og:image">`, as it's intended for crawlers (e.g. Bluesky, Twitter).
> - You can use `+Head` for `<meta name="description">`, as it's also for crawlers (e.g. Google).
>
> For a more detailed explanation, see <Link href="#only-html" />.


## Cumulative

The `+Head` setting is cumulative. For example:

```tsx
// /pages/+Head.ts
// Environment: server

import favicon from './favicon.png'

// Applies to all pages (cannot be overridden)
export const Head = () => (
  // All pages share the same favicon
  <link rel="icon" href={favicon} type="image/svg+xml" />
)
```

```tsx
// /pages/about-us/+Head.ts
// Environment: server

import previewImage from './previewImage.jpg'

export const Head = () => (
  // Both the favicon above and this tag applies to /pages/about-us/+Page.ts
  <meta property="og:image" content={previewImage} />
)
```

To apply different `+Head` settings to different pages:

```tsx
// /pages/(marketing)/+Head.ts
// Environment: server

import favicon from './favicon.png'

// Applies to all marketing pages
export const Head = () => <link rel="icon" href={favicon} type="image/svg+xml" />
```

```tsx
// /pages/admin/+Head.ts
// Environment: server

import favicon from './favicon.png'

// Applies to all admin pages
export const Head = () => <link rel="icon" href={favicon} type="image/svg+xml" />
```

> You can use <Link href="/config#clear-js">`.clear.js`</Link> and <Link href="/config#default-js">`.default.js`</Link> to control the inheritance chain.

> See also: <Link href="/config#inheritance"/>


## `<script>`

If you don't use Vue (e.g. React), you can use `+Head` for adding `<script>` tags to all your pages:

```tsx
// /pages/+Head.tsx

export function Head() {
  return (
    <>
      <script type="text/javascript" src="https://example.com/some-script.js"></script>
      <script>console.log("hello")</script>
    </>
  )
}
```

Adding a `<script>` tag to only one/some pages isn't currently supported: [#2244 - Add `<script>` to only one/some pages](https://github.com/vikejs/vike/issues/2244).

### Vue

If you use Vue, [`<script>` tags cannot be defined inside Vue SFC `<template>`](https://stackoverflow.com/questions/71430887/running-script-tags-within-template-on-vue-js/71442718#71442718). Instead use <Link href="/useConfig">`useConfig()`</Link>:

```vue
<script setup lang="ts">
import { h } from 'vue'
import { useConfig } from 'vike-vue/useConfig'
const config = useConfig()
config({
  Head: h('script', {
    type: 'text/javascript',
    src: 'https://example.com/some-script.js'
  })
})
</script>
```

See also: <Link href="#how-to-inject-raw-html" />


## `pageContext`

You can access <Link href="/pageContext">the `pageContext` object</Link> by using <Link href="/usePageContext">`usePageContext()`</Link>.

```tsx
// /pages/+Head.tsx

import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}

export function Head() {
  const pageContext = usePageContext()
  // ...
}
```


## How to inject raw HTML?

You can inject any arbitrary HTML string to the page's `<head>`. Examples using:

- <Link href="#react">React</Link>
- <Link href="#vue">Vue</Link>
- <Link href="#solid">Solid</Link>

<Warning>
Be cautious about the security risk called [XSS injections](https://en.wikipedia.org/wiki/Cross-site_scripting).
</Warning>

### React

You can use React's `dangerouslySetInnerHTML` to add raw HTML, for example:

```tsx
import React from 'react'
import { Head } from 'vike-react/Head'

function Image({ src, author }: { src: string, author: string }) {
  return (
    <>
      <img src={src} />
      <Head>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify({
              '@context': 'https://schema.org/',
              contentUrl: { src },
              creator: {
                '@type': 'Person',
                name: author
              }
            })
          }}
        ></script>
      </Head>
    </>
  )
}
```

### Vue

You can use <Link href="/useConfig">`useConfig()`</Link> with Vue's `innerHTML` to add raw HTML, for example:

```vue
<template>
  <img :src v-bind="otherAttrs" />
</template>

<script setup lang="ts">
import { useAttrs, h } from 'vue'
import { useConfig } from 'vike-vue/useConfig'

const { src, author, ...otherAttrs } = useAttrs() as Record<string, string>

const config = useConfig()
config({
  Head: h('script', {
    type: 'application/ld+json',
    innerHTML: JSON.stringify({
      '@context': 'https://schema.org/',
      contentUrl: { src },
      creator: {
        '@type': 'Person',
        name: author
      }
    })
  })
})
</script>
```

### Solid

You can use `innerHTML` to add raw HTML, for example:

```tsx
import { Head } from 'vike-solid/Head'

function Image({ src, author }: { src: string, author: string }) {
  return (
    <>
      <img src={src} />
      <Head>
        <script
          type="application/ld+json"
          innerHTML={JSON.stringify({
            '@context': 'https://schema.org/',
            contentUrl: { src },
            creator: {
              '@type': 'Person',
              name: author
            }
          })}
        ></script>
      </Head>
    </>
  )
}
```


## Only HTML

**Only applies to the first page's HTML**

The `+Head` component is only rendered for the HTML of the first page the user visits: the tags set by `+Head` aren't updated upon <Link href="/client-routing">client-side page navigation</Link>.

<div style={{height: 16}} />

**Limitation**

The most notable limitation is that the `+Head` setting cannot be used to set the value of the `<title>` tag, because its value won't update when navigating to a page with a different title.

> See the example below for a more detailed explanation.

Instead use the <Link href="/title">`+title`</Link> setting.

> For use cases where the `+Head` setting cannot be used, Vike offers tailored settings that update upon client-side navigation.

<div style={{height: 16}} />

**Only a small limitation**

This may seem like a major limitation but it actually isn't: you can use the `+Head` setting for the vast majority of use cases.

You can use `+Head` for setting `<head>` tags are read by HTML crawlers:

- Tags for social sites (Twitter, Instagram, ...) such as `<meta property="og:image">` (the preview image upon URL sharing).
  > Social site bots navigate your website only by using HTML requests: they don't execute client-side JavaScript and don't do client-side navigation.
- Tags for SEO such as `<meta name="description">`.
  > While Google can do client-side navigation, it still discovers `<head>` tags by using its HTML crawler.

You can use `+Head` for setting `<head>` tags that are global (they have the same value for all pages):

- Favicon.
  > Assuming all your pages share the same favicon (`<link rel="icon">`), there isn't any need to update the favicon upon client-side navigation.
- PWA settings.
  > PWA settings are global and there isn't any need to update them upon client-side navigation.
- `<script>`
  > Assuming the script applies to all your pages.

<div style={{height: 16}} />

**Example**

The following example showcases that using `+Head` for setting `<title>` doesn't work, while it does work for setting `<meta name="description">`.

```tsx
// /pages/index/+Head.tsx
// Environment: server

function Head() {
  return (
    <>
      <title>AwesomeRockets</title>
      <meta name="description" content="The rocket company." />
    </>
  )
}
```
```tsx
// /pages/about/+Head.tsx
// Environment: server

function Head() {
  return (
    <>
      <title>About us</title>
      <meta name="description" content="We deliver payload to space." />
    </>
  )
}
```

If the first URL the user visits is `/` then the rendered HTML is:

```html
<!DOCTYPE html>
<html>
  <head>
    <title>AwesomeRockets</title>
    <meta name="description" content="The rocket company.">
  </head>
</html>
```

If the user then clicks on a link `<a href="/about">About us</a>`, then Vike does client-side navigation and the page's title isn't updated: the browser sill shows `Welcome` even though the URL is now `/about`. That's because the <Link href="/client-routing">HTML isn't used upon client-side navigation (DOM manipulations are made instead)</Link> while `+Head` is only used when generating HTML.

> The `+Head` component is only loaded on the server-side and only used when rendering HTML of the first page by design.

This isn't an issue for the `<meta name="description">` tag because it's intended for search engines bots which
crawl your website using HTML.


## See also

- <Link href="/head-tags" />
- <Link href="/settings#html-shell" doNotInferSectionTitle />
